package upload

import (
	"context"
	"errors"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/gin-gonic/gin"
	"github.com/tencentyun/cos-go-sdk-v5"

	"gin-api-server/core_library/global"
	"gin-api-server/core_library/library/webengine/resp"
	"gin-api-server/utils/common"
	"gin-api-server/utils/encrty"
	"gin-api-server/utils/path"
)

// Action @noauth
// @Summary 上传文件
// @Tags  Mis
// @Accept  json
// @Produce json
// @Param file formData file true "文件"
// @Router /mis/upload [get]
// @Success 200 {object} UploadFileResponse
func Action(c *gin.Context) {
	file, err := c.FormFile("file")
	if err != nil {
		resp.FailWithMessage(c, "读取文件失败")
		return
	}
	maxSize := global.Config.Upload.MaxSize * 1024 * 1024
	if file.Size > int64(maxSize) {
		resp.FailWithMessage(c, "文件大小超过限制")
		return
	}
	var url, fullurl string
	switch global.Config.Upload.Type {
	case "local":
		url, fullurl, err = LocalUploadFile(file)
		if err != nil {
			resp.FailWithMessage(c, "上传文件失败(local) -> "+err.Error())
			return
		}
	case "cos":
		url, fullurl, err = CosUploadFile(file)
		if err != nil {
			resp.FailWithMessage(c, "上传文件失败(cos) -> "+err.Error())
			return
		}
	case "oss":
		url, fullurl, err = OssUploadFile(file)
		if err != nil {
			resp.FailWithMessage(c, "上传文件失败(oss) -> "+err.Error())
			return
		}
	case "qiniu":
		resp.FailWithMessage(c, "暂不支持七牛云")
		return
	default:
		resp.FailWithMessage(c, "config.upload.type is not set")
		return
	}

	resp.OkWithData(c, UploadFileResponse{
		Url:      url,
		FullUrl:  fullurl,
		FileName: file.Filename,
		Size:     file.Size,
	})

}

// LocalUploadFile 本地上传
func LocalUploadFile(file *multipart.FileHeader) (string, string, error) {

	if global.Config.Upload.Local.Chunking == false {

		savepath := global.Config.Upload.Local.SavePath + ReplaceSaveKey(global.Config.Upload.SaveKey, file.Filename)
		p := global.RootPath + savepath
		// 检查目录是否存在
		dirs := strings.Split(p, "/")
		dir := strings.Join(dirs[:len(dirs)-1], "/")
		if path.FileExist(dir) == false {
			err := os.MkdirAll(dir, os.ModePerm)
			if err != nil {
				return "", "", err
			}
		}
		// 读取上传的文件
		f, err := file.Open()
		if err != nil {
			return "", "", err
		}
		defer f.Close()
		// 创建文件
		out, err := os.Create(p)
		if err != nil {
			return "", "", err
		}
		defer out.Close()
		_, copyErr := io.Copy(out, f)
		if copyErr != nil {
			return "", "", copyErr
		}
		return savepath, common.CdnLocal(savepath), nil
	}

	return "", "", nil

}

// CosUploadFile 腾讯云上传
func CosUploadFile(file *multipart.FileHeader) (string, string, error) {
	cosConfig := global.Config.Upload.Tencent
	// 上传到cos
	u, _ := url.Parse(cosConfig.BaseURL)
	b := &cos.BaseURL{BucketURL: u}
	client := cos.NewClient(b, &http.Client{
		Transport: &cos.AuthorizationTransport{
			SecretID:  cosConfig.SecretID,
			SecretKey: cosConfig.SecretKey,
		},
	})
	key := cosConfig.PathPrefix + ReplaceSaveKey(global.Config.Upload.SaveKey, file.Filename)

	f, err := file.Open()
	if err != nil {
		return "", "", err
	}
	defer f.Close()
	response, err := client.Object.Put(context.Background(), key, f, nil)
	if err != nil {
		return "", "", err
	}
	if response.StatusCode == 200 {
		return key, cosConfig.BaseURL + key, nil
	} else {
		return "", "", errors.New("上传失败")
	}
}

// OssUploadFile 阿里云上传
func OssUploadFile(file *multipart.FileHeader) (string, string, error) {

	ossConfig := global.Config.Upload.Ali
	client, err := oss.New(ossConfig.Endpoint, ossConfig.AccessKeyId, ossConfig.AccessKeySecret)
	if err != nil {
		return "", "", err
	}
	bucket, err := client.Bucket(ossConfig.BucketName)
	if err != nil {
		return "", "", err
	}
	f, err := file.Open()
	if err != nil {
		return "", "", err
	}
	defer f.Close()
	key := ossConfig.BasePath + ReplaceSaveKey(global.Config.Upload.SaveKey, file.Filename)
	err = bucket.PutObject(strings.TrimPrefix(key, "/"), f)
	if err != nil {
		return "", "", err
	}
	return key, ossConfig.BucketUrl + key, nil
}

// QiniuUploadFile   七牛云上传
func QiniuUploadFile(file *multipart.FileHeader) (string, string, error) {

	return "", "", nil
}

// ReplaceSaveKey 替换保存路径中的变量
func ReplaceSaveKey(saveKey string, fileName string) string {
	ext := path.Ext(fileName)
	fileName = strings.TrimSuffix(fileName, path.Ext(fileName))
	saveKeys := map[string]string{
		"{year}":   time.Now().Format("2006"),
		"{month}":  time.Now().Format("01"),
		"{day}":    time.Now().Format("02"),
		"{hour}":   time.Now().Format("15"),
		"{minute}": time.Now().Format("04"),
		"{second}": time.Now().Format("05"),
		"{ext}":    ext,
		"{name}":   fileName,
		"{md5}":    encrty.Md5(fileName) + "_" + time.Now().Format("150405"),
	}
	for key, val := range saveKeys {
		saveKey = strings.Replace(saveKey, key, val, -1)
	}
	return saveKey
}
